package database

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	driver "github.com/go-sql-driver/mysql"
	"gorm.io/gorm"

	"github.com/samaa/photosalesplus/backend/internal/config"
)

func ensureDatabase(cfg *config.Config) error {
	dsnCfg, err := driver.ParseDSN(cfg.MySQLDSN)
	if err != nil {
		return fmt.Errorf("parse mysql dsn: %w", err)
	}

	if dsnCfg.DBName == "" {
		return fmt.Errorf("mysql dsn must include database name")
	}

	baseCfg := *dsnCfg
	baseCfg.DBName = ""
	baseDSN := baseCfg.FormatDSN()

	sqlDB, err := sql.Open("mysql", baseDSN)
	if err != nil {
		return fmt.Errorf("connect to mysql server: %w", err)
	}
	defer sqlDB.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := sqlDB.PingContext(ctx); err != nil {
		return fmt.Errorf("ping mysql server: %w", err)
	}

	query := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", dsnCfg.DBName)
	if _, err := sqlDB.ExecContext(ctx, query); err != nil {
		return fmt.Errorf("create database %s: %w", dsnCfg.DBName, err)
	}

	return nil
}

func runMigrations(db *gorm.DB) error {
	migrationsDir := filepath.Join("database", "migrations")
	entries, err := os.ReadDir(migrationsDir)
	if err != nil {
		return fmt.Errorf("read migrations: %w", err)
	}

	names := make([]string, 0, len(entries))
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}
		names = append(names, entry.Name())
	}
	sort.Strings(names)

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	for _, name := range names {
		content, err := os.ReadFile(filepath.Join(migrationsDir, name))
		if err != nil {
			return fmt.Errorf("load migration %s: %w", name, err)
		}

		statements := splitStatements(string(content))
		for _, stmt := range statements {
			if strings.TrimSpace(stmt) == "" {
				continue
			}
			if err := db.WithContext(ctx).Exec(stmt).Error; err != nil {
				return fmt.Errorf("execute migration %s: %w", name, err)
			}
		}
	}

	return nil
}

func splitStatements(sqlText string) []string {
	parts := strings.Split(sqlText, ";")
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}
		out = append(out, part)
	}
	return out
}
